use std::io;
use base64::{alphabet, Engine, engine};
use base64::engine::general_purpose;
use base64::engine::general_purpose::{STANDARD, URL_SAFE};
use base64::prelude::BASE64_STANDARD;
use base64::read::DecoderReader;
use base64::write::EncoderWriter;

// base64 使用参考：     https://docs.rs/base64/0.22.1/base64
// 将base64编码和解码为字节或utf8，快速且可配置的base64解码和编码。Base64在只允许纯文本的上下文中高效地传输二进制数据。
pub fn base64_demo() {

    // 1. 使用引擎对base64进行解码或编码，使用最适合您的应用程序的base64字母表和填充行为进行配置。

    // 2、引擎设置
    // 将字节流编码为“base64”的方法不止一种。不同的应用程序使用不同的编码字母和填充行为。

    // 3、编码字母表
    /*
    几乎所有的base64字母表都使用A-Z、A-Z和0-9，这给出了近64个字符（26+26+10=62），但它们在最后2个字符的选择上有所不同。
    大多数应用程序使用RFC 4648中指定的标准字母表。如果这就是您所需要的，您可以通过使用预配置的STANDARD引擎快速开始，如果您希望使用最小的占用空间，该引擎也可以在这里所示的前奏模块中使用。
    */
    // alphabet 字母模块中提供了其他常用字母。
    let encoded = BASE64_STANDARD.decode(b"+uwgVQA=").unwrap();
    println!("encoded: {:?}", encoded);
    assert_eq!(BASE64_STANDARD.decode(b"+uwgVQA=").unwrap(), b"\xFA\xEC\x20\x55\0");
    assert_eq!(BASE64_STANDARD.encode(b"\xFF\xEC\x20\x55\0"), "/+wgVQA=");

    // 4、URL安全字母表
    /*
    标准字母表使用 + 和 / 作为其两个非字母数字标记，如果不将其编码为 %2B 和 %2F，则无法在URL中安全使用。
    为了避免这种情况，一些应用程序使用“URL安全”字母表，该字母表使用 - 和 _ 要使用该替代字母表，请使用URL_SAFE引擎。这个例子没有使用序言来展示更明确的用法是什么样子的。
    */
    assert_eq!(URL_SAFE.decode(b"-uwgVQA=").unwrap(), b"\xFA\xEC\x20\x55\0");
    assert_eq!(URL_SAFE.encode(b"\xFF\xEC\x20\x55\0"), "_-wgVQA=");
    let bytes_url = engine::GeneralPurpose::new(
        &alphabet::URL_SAFE,
        general_purpose::NO_PAD)
        .decode("aGVsbG8gaW50ZXJuZXR-Cg").unwrap();
    println!("bytes_url: {:?}", bytes_url);

    // 5、填充字符
    /*
    每个base64字符代表原始二进制数据的6位（2⁶=64），每3个字节的输入二进制数据将编码为4个base64字符（8位×3=6位×4=24位）。
    当输入长度不是3字节的偶数倍时，规范的base64编码器会在末尾插入填充字符，这样输出长度总是4的倍数：
    */
    assert_eq!(STANDARD.encode(b""),    "");
    assert_eq!(STANDARD.encode(b"f"),   "Zg==");
    assert_eq!(STANDARD.encode(b"fo"),  "Zm8=");
    assert_eq!(STANDARD.encode(b"foo"), "Zm9v");

    // 6、进一步定制 base64 解码器和编码器
    // 通过创建具有字母表和填充配置的引擎，可以定制解码和编码行为：
    // 创建一个自定义的字母表，将 + 和 / 作为第一个字符而不是最后一个字符。这意味着只能编码下面这些字符： A-Z、a-z、0-9、+、/
    let alphabet =
        alphabet::Alphabet::new("+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
            .unwrap();

    // 一个非常奇怪的配置，使用填充进行编码，但在解码时不需要填充......？
    // 为引擎创建一个自定义配置，允许解码时允许尾部位，并在编码时使用填充。
    let crazy_config = engine::GeneralPurposeConfig::new()
        // 允许解码时尾部位
        .with_decode_allow_trailing_bits(true)
        // 使用填充进行编码
        .with_encode_padding(true)
        // 禁止解码时填充
        .with_decode_padding_mode(engine::DecodePaddingMode::RequireNone);
    // 创建一个新的引擎，使用自定义配置
    let crazy_engine = engine::GeneralPurpose::new(&alphabet, crazy_config);
    // 使用自定义引擎编码
    let encoded = crazy_engine.encode(b"abc 123");
    println!("encoded: {}", encoded);
    // 使用自定义引擎解码
    let decoded = crazy_engine.decode(encoded.as_bytes()).unwrap();
    println!("decoded: {}", String::from_utf8(decoded).unwrap());


    // base64 编码
    let mut output = io::stdout();
    // 编码器
    let mut encoder = EncoderWriter::new(&mut output, &STANDARD);
    io::copy(&mut io::stdin(), &mut encoder).unwrap();

    // base64 解码
    let mut input = io::stdin();
    let mut decoder = DecoderReader::new(&mut input, &STANDARD);
    io::copy(&mut decoder, &mut io::stdout()).unwrap();
}